﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text.RegularExpressions;
using DotNet.DevOps.Tasks.Util;

namespace DotNet.DevOps.Tasks.Service.Helpers
{
    internal class ServiceManager
    {
        public string Machine { get; set; }

        public string Name { get; set; }

        public string Description { get; set; }

        public string ExecutablePath { get; set; }
        public string ExecutableArguments { get; set; }

        public string LogonUser { get; set; }
        public string LogonPassword { get; set; }

        public string Startup { get; set; }

        public int CommandTimeout { get; set; }

        private ILog Log { get; set; }

        private readonly Regex failedRegex = new Regex(@"^.*[SC].*\sFAILED\s.*:.*$", RegexOptions.Compiled | RegexOptions.Multiline);

        public ServiceManager(ILog log)
        {
            CommandTimeout = -1;
            Name = "";
            Startup = "auto";
            Log = log;
        }

        public void UnInstall()
        {
            var commandArguments = string.Format("\\\\{0} delete {1}", Machine, Name);

            Log.LogInfo(string.Format("{0} - Uninstalling.", Name));

            RunCommand(commandArguments);

            Log.LogInfo(string.Format("{0} - Uninstall succeeded.", Name));
        }

        public void SetDescription()
        {
            var commandArguments = string.Format(@"\\{0} description {1} ""{2}""", Machine, Name, (string.IsNullOrEmpty(Description) ? Name : Description));

            Log.LogInfo(string.Format("{0} - Updating description.", Name));

            RunCommand(commandArguments);

            Log.LogInfo(string.Format("{0} - Description update succeeded.", Name));
        }

        public void Install()
        {
            var commandArguments = string.Format(@"\\{0} create {1} start= {2} binpath= ""{3}""", Machine, Name, GetStartupType(), GenerateBinPath());
            commandArguments = AppendLogonInformation(commandArguments);

            Log.LogInfo(string.Format("{0} - Installing.", Name));

            RunCommand(commandArguments);

            Log.LogInfo(string.Format("{0} - Install succeeded.", Name));
        }

        private string GetStartupType()
        {
            var validValues = new List<string> { "auto", "demand", "disabled" };

            if(validValues.Contains(Startup.ToLower()) == false)
            {
                throw new Exception(string.Format("Unknown Startup type specified: {0}. Should be one of: {1}",Startup , string.Join<string>(",",validValues)));
            }

            return Startup.ToLower();
        }

        private string GenerateBinPath()
        {
            return string.Format("\\\"{0}\\\" {1}", ExecutablePath, (string.IsNullOrWhiteSpace(ExecutableArguments) == true ? "" : ExecutableArguments.Replace(@"""","\\\"")));
        }

        private string AppendLogonInformation(string commandArguments)
        {
            if(string.IsNullOrWhiteSpace(LogonUser) == false)
            {
                return string.Format("{0} obj= \"{1}\" password= \"{2}\"", commandArguments, GetLogonUser(), LogonPassword);
            }
            return commandArguments;
        }

        private string GetLogonUser()
        {
            if(LogonUser.Contains("\\") == false)
            {
                return string.Format("{0}\\{1}", Machine, LogonUser);
            }
            return LogonUser;
        }

        private void RunCommand(string arguments)
        {
            var output = new Command("sc.exe", arguments, CommandTimeout).Run();

            ValidateOutput(output, new StackFrame(1).GetMethod().Name);
        }

        private void ValidateOutput(string output, string serviceAction)
        {
            var matches = failedRegex.Matches(output);

            if(matches.Count > 0)
            {
                var msg = output.Replace(matches[0].Value, "")
                                .Replace('\r', ' ')
                                .Replace('\r', ' ')
                                .Trim();

                Log.LogError(new Exception(msg));

                throw new Exception(string.Format("{0} failed.", serviceAction));
            }
        }
    }
}
